//!
/**
 ***********************************************************************************************************
 * <RBGeometry implementation>
 ***********************************************************************************************************
 * @file RBGeometry.cpp
 ***********************************************************************************************************/

#include "RBMainPCH.h"

/************************************************************************************************************
 * Includes
 ************************************************************************************************************/

#include "RBGeometry.h"

#include "RBHandle.h"
#include "RBHardwareBufferManager.h"
#include "RBLight.h"
#include "RBRenderer.h"
#include "RBStream.h"
#include "RBVisibilityManager.h"
#include "RBVertexElement.h"

/************************************************************************************************************
 * Macros
 ************************************************************************************************************/

/************************************************************************************************************
 * Classes implementation
 ************************************************************************************************************/

RB_IMPLEMENT_COMPONENT(RBGeometry);


/************************************************************************************************************/

// <Write implementation here>

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
RBGeometry::RBGeometry(RBEntityHandle entityHandle, RBTransformComponentHandle xForm)
: RBTSpatialComponent(entityHandle, xForm)
, m_spMaterial       (NULL)
, m_ePrimitiveType   (RBGeometryPrimitiveType::TRIANGLELIST)
, IsShadowCaster     (true)
, IsReflective       (true)
, IsVisible          (true)
, VertexStart        (0)
, VertexCount        (0)
, VertexDeclaration  (NULL)
, VertexBuffer       (NULL)
, IndexBuffer        (NULL)
, IndexStart         (0)
, IndexCount         (0)
{
}

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
RBGeometry::~RBGeometry()
{
}

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
void RBGeometry::CopyMembers(RBGeometryHandle dest)
{
    dest->SetMaterial(m_spMaterial);
    dest->SetPrimitiveType(m_ePrimitiveType);
    dest->IsShadowCaster = IsShadowCaster;
    dest->IsReflective = IsReflective;
    dest->IsVisible = IsVisible;
    dest->Priority = Priority;
    dest->VertexStart = VertexStart;
    dest->VertexCount = VertexCount;
    dest->VertexDeclaration = VertexDeclaration;
    dest->VertexBuffer = VertexBuffer;
    dest->IndexBuffer = IndexBuffer;
    dest->IndexStart = IndexStart;
    dest->IndexCount = IndexCount;

    dest->DataContainer = DataContainer;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBGeometry::Serialize(RBStream& rbStream)
{
    rbStream & m_ePrimitiveType;
    rbStream.Link(m_spMaterial);
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
RBGeometryHandle RBGeometry::CreateGeometry(uint32 uiVertexCount, uint32 uiIndexCount, float3* pPositions, uint16* pIndices, 
                                         RBGeometryPrimitiveType::EType ePT, RBEffectPass* pEffectPass, float3* pNormals, float2* pTextures, uint32 uiTextureSetCount, 
                                         float3* pColors, float3* pBinormals, float3* pTangents, bool bIsVBDynamic, bool bIsIBDynamic)
{
    static const uint32 ms_uiMaxElementCount = 8;
    RBVertexElement aElements[ms_uiMaxElementCount];
    void* pDatas[ms_uiMaxElementCount];
    uint32 uiElementIdx = 0;
    pDatas[uiElementIdx] = pPositions;
    aElements[uiElementIdx++] = RBVertexElement(0, RBVertexElement::VET_FLOAT3, RBVertexElement::VEU_POSITION);
    if (pTextures)
    {
        for (uint8 uiTextureSet = 0; uiTextureSet < uiTextureSetCount; ++uiTextureSet)
        {
            pDatas[uiElementIdx] = &pTextures[uiVertexCount * uiTextureSet];
            aElements[uiElementIdx++] = RBVertexElement(0, RBVertexElement::VET_FLOAT2, RBVertexElement::VEU_TEXCOORD);
        }
    }

    if (pColors)
    {
        pDatas[uiElementIdx] = pColors;
        aElements[uiElementIdx++] = RBVertexElement(0, RBVertexElement::VET_D3DCOLOR, RBVertexElement::VEU_COLOR);
    }

    if (pNormals)
    {
        pDatas[uiElementIdx] = pNormals;
        aElements[uiElementIdx++] = RBVertexElement(0, RBVertexElement::VET_FLOAT3, RBVertexElement::VEU_NORMAL);
    }

    if (pBinormals)
    {
        pDatas[uiElementIdx] = pBinormals;
        aElements[uiElementIdx++] = RBVertexElement(0, RBVertexElement::VET_FLOAT3, RBVertexElement::VEU_BITANGENT);
    }

    if (pTangents)
    {
        pDatas[uiElementIdx] = pTangents;
        aElements[uiElementIdx++] = RBVertexElement(0, RBVertexElement::VET_FLOAT3, RBVertexElement::VEU_TANGENT);
    }

    RBVertexDeclarationPtr spVertexDecl = RBRenderer::GetSingleton()->GetHBMgr()->CreateVertexDeclaration(aElements, uiElementIdx, pEffectPass);

    uint32 uiVtxSize = spVertexDecl->ComputeVertexSize();
    char* pData = new char[uiVtxSize * uiVertexCount];
    char* pCurData = pData;
    for (uint32 ui=0; ui < uiVertexCount; ++ui)
    {
        for (uint32 uiElement = 0; uiElement < uiElementIdx; ++uiElement)
        {
            char* pElementData = (char*)pDatas[uiElement];
            uint32 uiElementSize = aElements[uiElement].GetSize();
            memcpy_s(pCurData, uiVtxSize, &pElementData[ui * uiElementSize], uiElementSize);
            pCurData += uiElementSize;
        }
    }
    RBVertexBufferPtr spVB = RBRenderer::GetSingleton()->GetHBMgr()->CreateVertexBuffer(bIsVBDynamic ? rb_hw_buffer_usage::HBU_CPUWRITE :
                                                                                                       rb_hw_buffer_usage::HBU_DEFAULT, 
                                                                                        uiVtxSize, uiVertexCount, pData);
    delete[] pData;
    RBIndexBufferPtr spIB = 0;
    
    if (uiIndexCount > 0 && pIndices != NULL)
    {
        spIB = RBRenderer::GetSingleton()->GetHBMgr()->CreateIndexBuffer(bIsIBDynamic ? rb_hw_buffer_usage::HBU_CPUWRITE : 
                                                                                        rb_hw_buffer_usage::HBU_DEFAULT, 
                                                                         RBIndexBufferType::IT_16BIT, uiIndexCount, pIndices);
    }
    
    RBMaterialPtr spMaterial = new RBMaterial();

    RBHandle<RBGeometry> spGeom = RBGeometry::Create(InvalidHandle);
    spGeom->IndexBuffer = spIB;
    spGeom->IndexCount = uiIndexCount;
    spGeom->VertexBuffer = spVB;
    spGeom->VertexDeclaration = spVertexDecl;
    spGeom->VertexCount = uiVertexCount;
    spGeom->SetMaterial(spMaterial);
    spGeom->SetPrimitiveType(ePT);


    return spGeom;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBGeometryHandle RBGeometry::CreateGeometry( RBVertexBuffer* pVB, RBVertexDeclaration* pVtxDecl, RBIndexBuffer* pIB, RBGeometryPrimitiveType::EType ePT )
{
    RBHandle<RBGeometry> pGeom = RBGeometry::Create(InvalidHandle, RBTransformComponent::Create(InvalidHandle));
    pGeom->SetVertexData(pVtxDecl, pVB, pVB->GetVertexCount());
    pGeom->SetIndexData(pIB, pIB->GetIndexCount());
    RBMaterialPtr spMaterial = new RBMaterial();
    pGeom->SetMaterial(spMaterial);
    pGeom->SetPrimitiveType(ePT);

    return pGeom;
}


void RBGeometry::SetVertexData( RBVertexDeclaration* vertex_decl, RBVertexBuffer* vb, uint32 vertex_count, uint32 vertex_start )
{
    VertexDeclaration = vertex_decl;
    VertexBuffer = vb;
    VertexCount = vertex_count;
    VertexStart = vertex_start;
}


void RBGeometry::SetIndexData( RBIndexBuffer* index_buffer, uint32 index_count, uint32 index_start )
{
    IndexBuffer = index_buffer;
    IndexCount = index_count;
    IndexStart = index_start;
}



#define V(pMember) \
    if ((eCT = pMember->Accept(kVisitor)) != EVisitorCode::CT_CONTINUE) { return eCT; }
